﻿using CodeGen.TS.Generation.Members;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace CodeGen.TS.Generation
{
    public static partial class CodeGenerationHelper
    {

        public static IList<TSMethodInfo> GetTSMethods(this ICodeGenerator generator, bool withRecursion)
        {
            List<MethodInfo> methodInfos = new List<MethodInfo>();
            var type = generator.Type;

            while (type != null)
            {

                methodInfos.InsertRange(0, type.GetMethods(BindingFlags.Instance |
                    BindingFlags.DeclaredOnly |
                     BindingFlags.Public));
                if (generator.BaseTypeMode != BaseTypeMode.Include)
                {
                    break;
                }
                type = type.BaseType;
            }

            return GenerateMethods(generator, methodInfos, generator.UseObservable, withRecursion);
        }

        public static IList<TSMethodInfo> GenerateMethods(ICodeGenerator baseGenerator, IEnumerable<MethodInfo> methods, bool useObservable, bool withRecursion)
        {
            List<TSMethodInfo> infos = new List<TSMethodInfo>();
            foreach (var method in methods)
            {
                if (method.ReturnType == typeof(object))//It is likely to work without this as well, but to output object is really too much
                    continue;

                var info = new TSMethodInfo(method);
                SetScriptType(info, method.ReturnType, withRecursion, useObservable, baseGenerator);
                var parameters = method.GetParameters();
                foreach (var parameter in parameters)
                {
                    var parameterMember = new TSParameterInfo(parameter);
                    SetScriptType(parameterMember, parameter.ParameterType, withRecursion, useObservable, baseGenerator);
                    info.Parameters.Add(parameterMember);
                }

                infos.Add(info);

            }
            return infos;
        }

        public static bool IsNumberType(this Type input)
        {
            return (input == typeof(int) || input == typeof(int?)) ||
                (input == typeof(double) || input == typeof(double?)) ||
                (input == typeof(decimal) || input == typeof(decimal?)) ||
                (input == typeof(byte) || input == typeof(byte?)) ||
                (input == typeof(Int64) || input == typeof(Int64?)) ||
                (input == typeof(short) || input == typeof(short?));
        }

        public static void SetScriptType(IMemberInfo info, Type type, bool withRecursion, bool useObservable, ICodeGenerator baseGenerator)
        {
            info.OriginalCLRType = type;
            info.UseObservable = useObservable;

            //IEnumerable, arrays and so on can be represented by arrays in JavaScript/TypeScript
            if (type.IsGenericType && type.GetGenericTypeDefinition() ==
                typeof(IEnumerable<>))
            {
                info.IsArray = true;
                type = type.GetGenericArguments()[0];
            }
            else if (type.IsArray)
            {
                info.IsArray = true;
                type = type.GetElementType();

            }
            else
            {
                info.IsArray = false;
            }


            if (type == typeof(bool) || type == typeof(bool?))
            {
                info.ScriptType = IntegratedTypescriptType.Bool;
            }
            else if (type.IsNumberType())
            {
                info.ScriptType = IntegratedTypescriptType.Number;
            }
            else if (type == typeof(DateTime) || type == typeof(DateTime?))
            {
                info.ScriptType = IntegratedTypescriptType.Date;
            }
            else if (type == typeof(string))
            {
                info.ScriptType = IntegratedTypescriptType.String;

            }
            else if (withRecursion && !type.IsGenericType)//Generics are not part of typesscript yet
            {

                var generator = Output.TypeGeneratorFactory.GetGenerator(type, useObservable);

                if (generator == null)
                {
                    if (!useObservable)
                    {
                        generator = new Generation.Generators.InterfaceGenerator();
                        generator.Type = type;

                        generator.ModulName = type.Namespace;
                        generator.NewTypeName = "I" + type.Name;

                        baseGenerator.CopyToDependend(generator, false);
                        Output.TypeGeneratorFactory.RegisterGenerator(generator);
                    }
                    else
                    {
                        generator = new Generation.Generators.KnockoutObservableGenerator();
                        generator.Type = type;
                        generator.ModulName = null;
                        baseGenerator.CopyToDependend(generator);
                        Output.TypeGeneratorFactory.RegisterGenerator(generator);
                    }
                }
                if (generator != null)
                {
                    info.ScriptType = IntegratedTypescriptType.Complex;
                    info.ComplexTypeName = (generator.ModulName == null || generator.ModulName == baseGenerator.ModulName ? "" : generator.ModulName + ".") + generator.NewTypeName;

                }

            }
            else
            {
                info.ScriptType = IntegratedTypescriptType.Unknown;
            }

        }
    }
}
